package com.lechi.iot.client;

import com.lechi.iot.client.connect.AppClientConnect;
import com.lechi.iot.netty.channel.ITcpChannel;
import com.lechi.iot.netty.device.TcpDeviceChannel;
import com.lechi.iot.netty.handle.TcpExceptionHandler;
import com.lechi.iot.netty.utils.NettyCacheUtils;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * iot 采集盒 客户端
 */
@Slf4j
public class AppClient extends ChannelInitializer<SocketChannel> {

    private int heartBeatTime;

    private Map<String, Object> workerGroupMap = new HashMap<>();

    private Set<InetSocketAddress> addressSet = new HashSet<>();

    private Map<String, Object> cmMap = new HashMap<>();

    public AppClient(int heartBeatTime) {
        this.heartBeatTime = heartBeatTime;
    }


    /**
     * 连接服务端
     */
    public void connect(String serverHost, int serverPort, int reconnectTime) throws Exception {
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        workerGroupMap.put(serverHost, workerGroup);
        Bootstrap boot = new Bootstrap()
                .group(workerGroup)
                .channel(NioSocketChannel.class)
                .handler(this);
        InetSocketAddress address = InetSocketAddress.createUnresolved(serverHost, serverPort);
        addressSet.add(address);
        AppClientConnect appClientConnect = new AppClientConnect(boot, reconnectTime) {
            @Override
            public void afterSuccess() {
                log.info("和上位机{}:{}建立连接成功", serverHost, serverPort); // 新增连接成功日志
                registerGroup(serverHost);
            }
        };
        cmMap.put(serverHost, appClientConnect);
        appClientConnect.connect(address);
    }

    /**
     * 停止服务
     */
    public ChannelFuture disconnect(String serverHost) {
        log.error("采集器断开和服务端的连接地址：{}", serverHost);
        addressSet.clear();
        EventLoopGroup workerGroup = (EventLoopGroup) workerGroupMap.get(serverHost);
        workerGroup.shutdownGracefully();
        workerGroupMap.clear();
        cmMap.clear();
        return null;
    }

    @Override
    protected void initChannel(SocketChannel ch) {
        ChannelPipeline p = ch.pipeline();
        for (InetSocketAddress address : addressSet) {
            ITcpChannel channel = new TcpDeviceChannel(address, ch);
            ITcpChannel isExist = (ITcpChannel) NettyCacheUtils.get(channel.name());
            if (Objects.isNull(isExist)) {
                AppClientConnect cm = (AppClientConnect) cmMap.get(channel.name());
                p.addLast(cm);
                p.addLast(new IdleStateHandler(0, 0, heartBeatTime, TimeUnit.MILLISECONDS));
                p.addLast(new ChannelInboundHandlerAdapter() {
                    @Override
                    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                        if (evt instanceof IdleStateEvent) {
                          //  registerGroup(channel.name());
                        } else {
                            super.userEventTriggered(ctx, evt);
                        }
                    }
                });
                p.addLast(new DelimiterBasedFrameDecoder(2048, Unpooled.copiedBuffer(COMMAND.RESPONSE_END.getBytes())));
                p.addLast(new TcpExceptionHandler("采集器连接软件平台异常"));
            }
        }
    }

    private static void registerGroup(String ip) {
        ITcpChannel channel = (ITcpChannel) NettyCacheUtils.get(ip);
        if (Objects.isNull(channel)) {
            return;
        }
        ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer();
        buffer.writeCharSequence(COMMAND.getHeartMsg("FY001"), Charset.defaultCharset());
        channel.send(buffer);
    }
}
